home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 23
/
AACD 23.iso
/
AACD
/
Online
/
opennap
/
handler.c
< prev
next >
Wrap
C/C++ Source or Header
|
2001-06-08
|
27KB
|
699 lines
/* Copyright (C) 2000-1 drscholl@users.sourceforge.net
This is free software distributed under the terms of the
GNU Public License. See the file COPYING for details.
$Id: handler.c,v 1.112 2001/02/15 08:39:45 drscholl Exp $ */
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include "opennap.h"
#include "debug.h"
#if DEBUG
#include <ctype.h>
#endif
static HANDLER (histogram);
/* 214 */
HANDLER (server_stats)
{
(void) pkt;
(void) len;
(void) tag;
send_cmd (con, MSG_SERVER_STATS, "%d %d %d", Users->dbsize, Num_Files,
(int) (Num_Gigs / 1048576.));
}
/* 10018 :<server> <target> <packet>
allows a server to send an arbitrary message to a remote user */
HANDLER (encapsulated)
{
char *nick, ch, *ptr;
USER *user;
(void) tag;
ASSERT (validate_connection (con));
CHECK_SERVER_CLASS ("encapsulated");
if (*pkt != ':')
{
log ("encapsulated: server message does not begin with a colon (:)");
return;
}
nick = strchr (pkt + 1, ' ');
if (!nick)
{
log ("encapsulated: missing target nick");
return;
}
nick++;
ptr = strchr (nick, ' ');
if (!ptr)
{
log ("encapsulated: missing encapsulated packet");
return;
}
ch = *ptr;
*ptr = 0;
user = hash_lookup (Users, nick);
if (!user)
{
log ("encapsulated: no such user %s", nick);
return;
}
if (user->local)
{
ptr++;
queue_data (user->con, ptr, len - (ptr - pkt));
}
else
{
*ptr = ch;
/* avoid copying the data twice by peeking into the send buffer to
grab the message header and body together */
pass_message (con, con->recvbuf->data + con->recvbuf->consumed,
4 + len);
}
}
/* the windows napster client will hang indefinitely waiting for this, so
* return what it expects.
*/
static HANDLER (version_check)
{
(void) pkt;
(void) tag;
(void) len;
if (ISUSER (con))
send_cmd (con, MSG_CLIENT_VERSION_CHECK, "");
}
/* certain user commands need to be exempt from flood control or the server
* won't work correctly.
*/
#define F_EXEMPT 1 /* exempt from flood control */
typedef struct
{
unsigned int message;
HANDLER ((*handler));
unsigned int flags;
unsigned long count;
double bytes;
}
HANDLER;
#define NORMAL(a,b) {a,b,0,0,0}
#define EXEMPT(a,b) {a,b,F_EXEMPT,0,0}
/* this is the table of valid commands we accept from both users and servers
THIS TABLE MUST BE SORTED BY MESSAGE TYPE */
static HANDLER Protocol[] = {
NORMAL (MSG_SERVER_ERROR, server_error), /* 0 */
NORMAL (MSG_CLIENT_LOGIN, login), /* 2 */
NORMAL (MSG_CLIENT_VERSION_CHECK, version_check), /* 4 */
NORMAL (MSG_CLIENT_LOGIN_REGISTER, login), /* 6 */
NORMAL (MSG_CLIENT_REGISTER, register_nick), /* 7 */
NORMAL (MSG_CLIENT_CHECK_PASS, check_password), /* 11 */
NORMAL (MSG_CLIENT_REGISTRATION_INFO, ignore_command), /* 14 */
#ifndef ROUTING_ONLY
EXEMPT (MSG_CLIENT_ADD_FILE, add_file), /* 100 */
EXEMPT (MSG_CLIENT_REMOVE_FILE, remove_file), /* 102 */
#endif
NORMAL (MSG_CLIENT_UNSHARE_ALL, unshare_all), /* 110 */
#ifndef ROUTING_ONLY
NORMAL (MSG_CLIENT_SEARCH, search), /* 200 */
#endif
NORMAL (MSG_CLIENT_DOWNLOAD, download), /* 203 */
NORMAL (MSG_CLIENT_PRIVMSG, privmsg), /* 205 */
EXEMPT (MSG_CLIENT_ADD_HOTLIST, add_hotlist), /* 207 */
EXEMPT (MSG_CLIENT_ADD_HOTLIST_SEQ, add_hotlist), /* 208 */
NORMAL (MSG_CLIENT_BROWSE, browse), /* 211 */
NORMAL (MSG_SERVER_STATS, server_stats), /* 214 */
NORMAL (MSG_CLIENT_RESUME_REQUEST, resume), /* 215 */
NORMAL (MSG_CLIENT_DOWNLOAD_START, download_start), /* 218 */
NORMAL (MSG_CLIENT_DOWNLOAD_END, download_end), /* 219 */
NORMAL (MSG_CLIENT_UPLOAD_START, upload_start), /* 220 */
NORMAL (MSG_CLIENT_UPLOAD_END, upload_end), /* 221 */
NORMAL (MSG_CLIENT_CHECK_PORT, ignore_command), /* 300 */
NORMAL (MSG_CLIENT_REMOVE_HOTLIST, remove_hotlist), /* 303 */
NORMAL (MSG_CLIENT_IGNORE_LIST, ignore_list), /* 320 */
NORMAL (MSG_CLIENT_IGNORE_USER, ignore), /* 322 */
NORMAL (MSG_CLIENT_UNIGNORE_USER, unignore), /* 323 */
NORMAL (MSG_CLIENT_CLEAR_IGNORE, clear_ignore), /* 326 */
NORMAL (MSG_CLIENT_JOIN, join), /* 400 */
NORMAL (MSG_CLIENT_PART, part), /* 401 */
NORMAL (MSG_CLIENT_PUBLIC, public), /* 402 */
NORMAL (MSG_SERVER_PUBLIC, public), /* 403 */
NORMAL (MSG_SERVER_NOSUCH, server_error), /* 404 */
NORMAL (MSG_SERVER_TOPIC, topic), /* 410 */
NORMAL (MSG_CLIENT_CHANNEL_BAN_LIST, channel_banlist), /* 420 */
NORMAL (MSG_CLIENT_CHANNEL_BAN, channel_ban), /* 422 */
NORMAL (MSG_CLIENT_CHANNEL_UNBAN, channel_ban), /* 423 */
NORMAL (MSG_CLIENT_CHANNEL_CLEAR_BANS, channel_clear_bans), /* 424 */
NORMAL (MSG_CLIENT_DOWNLOAD_FIREWALL, download), /* 500 */
NORMAL (MSG_CLIENT_USERSPEED, user_speed), /* 600 */
NORMAL (MSG_CLIENT_WHOIS, whois), /* 603 */
NORMAL (MSG_CLIENT_SETUSERLEVEL, level), /* 606 */
NORMAL (MSG_SERVER_UPLOAD_REQUEST, upload_request), /* 607 */
NORMAL (MSG_CLIENT_UPLOAD_OK, upload_ok), /* 608 */
NORMAL (MSG_CLIENT_KILL, kill_user), /* 610 */
NORMAL (MSG_CLIENT_NUKE, nuke), /* 611 */
NORMAL (MSG_CLIENT_BAN, ban), /* 612 */
NORMAL (MSG_CLIENT_ALTER_PORT, alter_port), /* 613 */
NORMAL (MSG_CLIENT_UNBAN, unban), /* 614 */
NORMAL (MSG_CLIENT_BANLIST, banlist), /* 615 */
NORMAL (MSG_CLIENT_LIST_CHANNELS, list_channels), /* 618 */
NORMAL (MSG_CLIENT_LIMIT, queue_limit), /* 619 */
NORMAL (MSG_CLIENT_MOTD, show_motd), /* 621 */
NORMAL (MSG_CLIENT_MUZZLE, muzzle), /* 622 */
NORMAL (MSG_CLIENT_UNMUZZLE, muzzle), /* 623 */
NORMAL (MSG_CLIENT_ALTER_SPEED, alter_speed), /* 625 */
NORMAL (MSG_CLIENT_DATA_PORT_ERROR, data_port_error), /* 626 */
NORMAL (MSG_CLIENT_WALLOP, wallop), /* 627 */
NORMAL (MSG_CLIENT_ANNOUNCE, announce), /* 628 */
NORMAL (MSG_CLIENT_BROWSE_DIRECT, browse_direct), /* 640 */
NORMAL (MSG_SERVER_BROWSE_DIRECT_OK, browse_direct_ok), /* 641 */
NORMAL (MSG_CLIENT_CLOAK, cloak), /* 652 */
NORMAL (MSG_CLIENT_CHANGE_SPEED, change_speed), /* 700 */
NORMAL (MSG_CLIENT_CHANGE_PASS, change_pass), /* 701 */
NORMAL (MSG_CLIENT_CHANGE_EMAIL, change_email), /* 702 */
NORMAL (MSG_CLIENT_CHANGE_DATA_PORT, change_data_port), /* 703 */
NORMAL (MSG_CLIENT_PING_SERVER, ping_server), /* 750 */
NORMAL (MSG_CLIENT_PING, ping), /* 751 */
NORMAL (MSG_CLIENT_PONG, ping), /* 752 */
NORMAL (MSG_CLIENT_ALTER_PASS, alter_pass), /* 753 */
NORMAL (MSG_CLIENT_SERVER_RECONFIG, server_reconfig), /* 800 */
NORMAL (MSG_CLIENT_SERVER_VERSION, server_version), /* 801 */
NORMAL (MSG_CLIENT_SERVER_CONFIG, server_config), /* 810 */
NORMAL (MSG_CLIENT_CLEAR_CHANNEL, clear_channel), /* 820 */
NORMAL (MSG_CLIENT_REDIRECT, redirect_client), /* 821 */
NORMAL (MSG_CLIENT_CYCLE, cycle_client), /* 822 */
NORMAL (MSG_CLIENT_SET_CHAN_LEVEL, channel_level), /* 823 */
NORMAL (MSG_CLIENT_EMOTE, emote), /* 824 */
NORMAL (MSG_CLIENT_CHANNEL_LIMIT, channel_limit), /* 826 */
NORMAL (MSG_CLIENT_FULL_CHANNEL_LIST, full_channel_list), /* 827 */
NORMAL (MSG_CLIENT_KICK, kick), /* 829 */
NORMAL (MSG_CLIENT_NAMES_LIST, list_users), /* 830 */
NORMAL (MSG_CLIENT_GLOBAL_USER_LIST, global_user_list), /* 831 */
#ifndef ROUTING_ONLY
EXEMPT (MSG_CLIENT_ADD_DIRECTORY, add_directory), /* 870 */
#endif
NORMAL (920, ignore_command), /* 920 */
/* non-standard messages */
NORMAL (MSG_CLIENT_QUIT, client_quit), /* 10000 */
NORMAL (MSG_SERVER_LOGIN, server_login), /* 10010 */
NORMAL (MSG_SERVER_LOGIN_ACK, server_login_ack), /* 10011 */
NORMAL (MSG_SERVER_USER_SHARING, user_sharing), /* 10012 */
NORMAL (MSG_SERVER_REGINFO, reginfo), /* 10014 */
NORMAL (MSG_SERVER_REMOTE_SEARCH, remote_search), /* 10015 */
NORMAL (MSG_SERVER_REMOTE_SEARCH_RESULT, remote_search_result), /* 10016 */
NORMAL (MSG_SERVER_REMOTE_SEARCH_END, remote_search_end), /* 10017 */
NORMAL (MSG_SERVER_ENCAPSULATED, encapsulated), /* 10018 */
NORMAL (MSG_SERVER_LINK_INFO, link_info), /* 10019 */
NORMAL (MSG_SERVER_QUIT, server_disconnect), /* 10020 - deprecated by 10101 */
NORMAL (MSG_SERVER_NOTIFY_MODS, remote_notify_mods), /* 10021 */
NORMAL (MSG_SERVER_SERVER_PONG, server_pong), /* 10022 */
NORMAL (MSG_SERVER_TIME_CHECK, time_check), /* 10023 */
NORMAL (MSG_SERVER_WHOIS_NOTIFY, whois_notify), /* 10024 */
NORMAL (MSG_CLIENT_CONNECT, server_connect), /* 10100 */
NORMAL (MSG_CLIENT_DISCONNECT, server_disconnect), /* 10101 */
NORMAL (MSG_CLIENT_KILL_SERVER, kill_server), /* 10110 */
NORMAL (MSG_CLIENT_REMOVE_SERVER, remove_server), /* 10111 */
NORMAL (MSG_CLIENT_LINKS, server_links), /* 10112 */
NORMAL (MSG_CLIENT_USAGE_STATS, server_usage), /* 10115 */
NORMAL (MSG_CLIENT_REHASH, rehash), /* 10116 */
NORMAL (MSG_CLIENT_VERSION_STATS, client_version_stats), /* 10118 */
NORMAL (MSG_CLIENT_WHICH_SERVER, which_server), /* 10119 */
NORMAL (MSG_CLIENT_PING_ALL_SERVERS, ping_all_servers), /* 10120 */
NORMAL (MSG_CLIENT_WHO_WAS, who_was), /* 10121 */
NORMAL (MSG_CLIENT_MASS_KILL, mass_kill), /* 10122 */
NORMAL (MSG_CLIENT_HISTOGRAM, histogram), /* 10123 */
NORMAL (MSG_CLIENT_REGISTER_USER, register_user), /* 10200 */
NORMAL (MSG_CLIENT_USER_MODE, user_mode_cmd), /* 10203 */
NORMAL (MSG_CLIENT_OP, channel_op), /* 10204 */
NORMAL (MSG_CLIENT_DEOP, channel_op), /* 10205 */
NORMAL (MSG_CLIENT_CHANNEL_WALLOP, channel_wallop), /* 10208 */
NORMAL (MSG_CLIENT_CHANNEL_MODE, channel_mode), /* 10209 */
NORMAL (MSG_CLIENT_CHANNEL_INVITE, channel_invite), /* 10210 */
NORMAL (MSG_CLIENT_CHANNEL_VOICE, channel_op), /* 10211 */
NORMAL (MSG_CLIENT_CHANNEL_UNVOICE, channel_op), /* 10212 */
NORMAL (MSG_CLIENT_CHANNEL_MUZZLE, channel_muzzle), /* 10213 */
NORMAL (MSG_CLIENT_CHANNEL_UNMUZZLE, channel_muzzle), /* 10214 */
NORMAL (MSG_CLIENT_CLASS_ADD, generic_acl_add), /* 10250 */
NORMAL (MSG_CLIENT_CLASS_DEL, generic_acl_del), /* 10251 */
NORMAL (MSG_CLIENT_CLASS_LIST, generic_acl_list), /* 10252 */
NORMAL (MSG_CLIENT_DLINE_ADD, generic_acl_add),
NORMAL (MSG_CLIENT_DLINE_DEL, generic_acl_del),
NORMAL (MSG_CLIENT_DLINE_LIST, generic_acl_list),
NORMAL (MSG_CLIENT_ILINE_ADD, generic_acl_add),
NORMAL (MSG_CLIENT_ILINE_DEL, generic_acl_del),
NORMAL (MSG_CLIENT_ILINE_LIST, generic_acl_list),
NORMAL (MSG_CLIENT_ELINE_ADD, generic_acl_add),
NORMAL (MSG_CLIENT_ELINE_DEL, generic_acl_del),
NORMAL (MSG_CLIENT_ELINE_LIST, generic_acl_list),
#ifndef ROUTING_ONLY
EXEMPT (MSG_CLIENT_SHARE_FILE, share_file), /* 10300 */
#endif
#if 0
NORMAL (MSG_CLIENT_BROWSE_NEW, browse_new), /* 10301 */
#endif
};
static int Protocol_Size = sizeof (Protocol) / sizeof (HANDLER);
/* dummy entry used to keep track of invalid commands */
static HANDLER unknown_numeric = { 0, 0, 0, 0, 0 };
/* 10123
* report statistics for server commands.
*/
static HANDLER (histogram)
{
unsigned long count = 0;
double bytes = 0;
int l;
(void) pkt;
(void) len;
CHECK_USER_CLASS ("histogram");
if (con->user->level < LEVEL_ELITE)
{
permission_denied (con);
return;
}
for (l = 0; l < Protocol_Size; l++)
{
send_cmd (con, tag, "%d %u %.0f", Protocol[l].message,
Protocol[l].count, Protocol[l].bytes);
count += Protocol[l].count;
bytes += Protocol[l].bytes;
}
send_cmd (con, MSG_SERVER_HISTOGRAM, "%d %u %.0f %lu %.0f",
unknown_numeric.message, unknown_numeric.count,
unknown_numeric.bytes, count, bytes);
}
/* use a binary search to find the table in the entry */
static int
find_handler (unsigned int tag)
{
int min = 0, max = Protocol_Size - 1, try;
while (!SigCaught)
{
try = (max + min) / 2;
if (tag == Protocol[try].message)
return try;
else if (min == max)
return -1; /* not found */
else if (tag < Protocol[try].message)
{
if (try == min)
return -1;
max = try - 1;
}
else
{
if (try == max)
return -1;
min = try + 1;
}
ASSERT (min <= max);
}
return -1;
}
/* this is not a real handler, but takes the same arguments as one */
HANDLER (dispatch_command)
{
int l;
u_char byte;
ASSERT (validate_connection (con));
ASSERT (pkt != 0);
/* HACK ALERT
the handler routines all assume that the `pkt' argument is nul (\0)
terminated, so we have to replace the byte after the last byte in
this packet with a \0 to make sure we dont read overflow in the
handlers. the handle_connection() function should always allocate 1
byte more than necessary for this purpose */
ASSERT (VALID_LEN
(con->recvbuf->data, con->recvbuf->consumed + 4 + len + 1));
byte = *(pkt + len);
*(pkt + len) = 0;
l = find_handler (tag);
if (l != -1)
{
ASSERT (Protocol[l].handler != 0);
/* do flood control if enabled */
if (Flood_Time > 0 && !(Protocol[l].flags & F_EXEMPT) && ISUSER (con))
{
/* this command is subject to flood control. */
if (con->flood_start + Flood_Time < global.current_time)
{
/* flood expired, reset counters */
con->flood_start = global.current_time;
con->flood_commands = 0;
}
else if (++con->flood_commands >= Flood_Commands)
{
LIST *list;
log
("dispatch_command: flooding from %s!%s (numeric = %hu)",
con->user->nick, con->host, tag);
notify_mods (FLOODLOG_MODE,
"Flooding from %s!%s (numeric = %hu)",
con->user->nick, con->host, tag);
/* stop reading from the descriptor until the flood counter
* expires.
*/
clear_read (con->fd);
/* add to the list of flooders that is check in the main
* loop. Since we don't traverse the entire client list we
* have to keep track of which ones to check for expiration
*/
list = CALLOC (1, sizeof (LIST));
list->data = con;
Flooders = list_push (Flooders, list);
}
}
/* note that we pass only the data part of the packet */
Protocol[l].handler (con, tag, len, pkt);
Protocol[l].count++;
Protocol[l].bytes += len;
goto done;
}
log ("dispatch_command: unknown message: tag=%hu, length=%hu, data=%s",
tag, len, pkt);
unknown_numeric.message = tag;
unknown_numeric.count++;
unknown_numeric.bytes += len;
send_cmd (con, MSG_SERVER_NOSUCH, "Unknown command code %hu", tag);
#if DEBUG
/* if this is a server connection, shut it down to avoid flooding the
other server with these messages */
if (ISSERVER (con))
{
u_char ch;
int bytes;
/* dump some bytes from the input buffer to see if it helps aid
debugging */
bytes = con->recvbuf->datasize - con->recvbuf->consumed;
/* print at most 128 bytes */
if (bytes > 128)
bytes = 128;
fprintf (stdout, "Dump(%d): ",
con->recvbuf->datasize - con->recvbuf->consumed);
for (l = con->recvbuf->consumed; bytes > 0; bytes--, l++)
{
ch = *(con->recvbuf->data + l);
fputc (isprint (ch) ? ch : '.', stdout);
}
fputc ('\n', stdout);
}
#endif /* DEBUG */
done:
/* restore the byte we overwrite at the beginning of this function */
*(pkt + len) = byte;
}
void
handle_connection (CONNECTION * con)
{
int n;
u_short tag, len;
ASSERT (validate_connection (con));
if (ISSERVER (con))
{
/* server data is compressed. read as much as we can and pass it
to the decompressor. we attempt to read all data from the socket
in this loop, which will prevent unnecessary passes through the
main loop (since select would return immediately) */
do
{
n = READ (con->fd, Buf, sizeof (Buf));
if (n <= 0)
{
if (n == -1)
{
/* try to empty the socket each time, so we read until
* we hit this error (queue empty). this should only
* happen in the rare event that the data in the queue
* is a multiple of sizeof(Buf)
*/
if (N_ERRNO == EWOULDBLOCK)
break; /* not an error */
log
("handle_connection: read: %s (errno %d) for host %s (fd %d)",
strerror (N_ERRNO), N_ERRNO, con->host, con->fd);
}
else
log ("handle_connection: EOF from %s", con->host);
destroy_connection (con);
return;
}
if (global.min_read > 0 && n < global.min_read)
{
log ("handle_connection: %d bytes from %s", n,
con->host);
}
global.bytes_in += n;
/* this can safely be called multiple times in this loop. the
* decompressor will realloc the output buffer if there is not
* enough room to store everything
*/
if (buffer_decompress (con->recvbuf, con->sopt->zin, Buf, n))
{
destroy_connection (con);
return;
}
/* if what we read was equal to sizeof(Buf) it's very likely
* that more data exists in the queue
*/
}
while (n == sizeof (Buf));
}
else
{
/* create the input buffer if it doesn't yet exist */
if (!con->recvbuf)
{
con->recvbuf = CALLOC (1, sizeof (BUFFER));
if (!con->recvbuf)
{
OUTOFMEMORY ("handle_connection");
destroy_connection (con);
return;
}
#if DEBUG
con->recvbuf->magic = MAGIC_BUFFER;
#endif
con->recvbuf->data = MALLOC (5);
if (!con->recvbuf->data)
{
OUTOFMEMORY ("handle_connection");
destroy_connection (con);
return;
}
con->recvbuf->datamax = 4;
}
/* read the packet header if we haven't seen it already */
while (con->recvbuf->datasize < 4)
{
n = READ (con->fd, con->recvbuf->data + con->recvbuf->datasize,
4 - con->recvbuf->datasize);
if (n == -1)
{
if (N_ERRNO != EWOULDBLOCK)
{
log
("handle_connection: read: %s (errno %d) for host %s",
strerror (N_ERRNO), N_ERRNO, con->host);
destroy_connection (con);
}
return;
}
else if (n == 0)
{
destroy_connection (con);
return;
}
global.bytes_in += n;
con->recvbuf->datasize += n;
}
/* read the packet body */
memcpy (&len, con->recvbuf->data, 2);
len = BSWAP16 (len);
if (len > 0)
{
if (len > Max_Command_Length)
{
log ("handle_connection: %hu byte message from %s",
len, con->host);
destroy_connection (con);
return;
}
/* if there isn't enough space to read the entire body, resize the
input buffer */
if (con->recvbuf->datamax < 4 + len)
{
/* allocate 1 extra byte for the \0 that dispatch_command()
requires */
if (safe_realloc ((void **) &con->recvbuf->data, 4 + len + 1))
{
OUTOFMEMORY ("handle_connection");
destroy_connection (con);
return;
}
con->recvbuf->datamax = 4 + len;
}
n = READ (con->fd, con->recvbuf->data + con->recvbuf->datasize,
len + 4 - con->recvbuf->datasize);
if (n == -1)
{
/* since the header and body could arrive in separate packets,
we have to check for this here so we don't close the
connection on this nonfatal error. we just wait for the
next packet to arrive */
if (N_ERRNO != EWOULDBLOCK)
{
log
("handle_connection: read: %s (errno %d) for host %s",
strerror (N_ERRNO), N_ERRNO, con->host);
destroy_connection (con);
}
return;
}
else if (n == 0)
{
log ("handle_connection: EOF from %s", con->host);
destroy_connection (con);
return;
}
con->recvbuf->datasize += n;
global.bytes_in += n;
}
}
/* process as many complete commands as possible. for a client this
will be exactly one, but a server link may have sent multiple commands
in one compressed packet */
while (con->recvbuf->consumed < con->recvbuf->datasize)
{
/* if we don't have the complete packet header, wait until we
read more data */
if (con->recvbuf->datasize - con->recvbuf->consumed < 4)
break;
/* read the packet header */
memcpy (&len, con->recvbuf->data + con->recvbuf->consumed, 2);
memcpy (&tag, con->recvbuf->data + con->recvbuf->consumed + 2, 2);
len = BSWAP16 (len);
tag = BSWAP16 (tag);
/* check if the entire packet body has arrived */
if (con->recvbuf->consumed + 4 + len > con->recvbuf->datasize)
break;
/* require that the client register before doing anything else */
if (con->class == CLASS_UNKNOWN &&
(tag != MSG_CLIENT_LOGIN && tag != MSG_CLIENT_LOGIN_REGISTER &&
tag != MSG_CLIENT_REGISTER && tag != MSG_SERVER_LOGIN &&
tag != MSG_SERVER_LOGIN_ACK && tag != MSG_SERVER_ERROR &&
tag != 4 && /* unknown: v2.0 beta 5a sends this? */
tag != 300 && tag != 11 && tag != 920))
{
log ("handle_connection: %s is not registered", con->host);
*(con->recvbuf->data + con->recvbuf->consumed + 4 + len) = 0;
log ("handle_connection: tag=%hu, len=%hu, data=%s", tag, len,
con->recvbuf->data + con->recvbuf->consumed + 4);
#if 0
/* not sure why the official servers do this, but lets
* be compatible.
*/
send_cmd (con, MSG_SERVER_ECHO, "%hu: %s", tag,
con->recvbuf->data + con->recvbuf->consumed + 4);
con->recvbuf->consumed += 4 + len;
break;
#else
send_cmd (con, MSG_SERVER_ERROR, "invalid command");
destroy_connection (con);
return;
#endif
}
if (Servers && ISUSER (con))
{
/* check for end of share/unshare sequence. in order to avoid
having to send a single message for each shared file,
the add_file and remove_file commands set a flag noting the
start of a possible series of commands. this routine checks
to see if the end of the sequence has been reached (a command
other than share/unshare has been issued) and then relays
the final result to the peer servers.
NOTE: the only issue with this is that if the user doesn't
issue any commands after sharing files, the information will
never get passed to the peer servers. This is probably ok
since this case will seldom happen */
if (con->user->sharing)
{
if (tag != MSG_CLIENT_ADD_FILE
&& tag != MSG_CLIENT_SHARE_FILE
&& tag != MSG_CLIENT_ADD_DIRECTORY)
{
pass_message_args (con, MSG_SERVER_USER_SHARING,
"%s %hu %u", con->user->nick,
con->user->shared, con->user->libsize);
con->user->sharing = 0;
}
}
else if (con->user->unsharing)
{
if (tag != MSG_CLIENT_REMOVE_FILE)
{
pass_message_args (con, MSG_SERVER_USER_SHARING,
"%s %hu %u", con->user->nick,
con->user->shared, con->user->libsize);
con->user->unsharing = 0;
}
}
}
/* call the protocol handler */
dispatch_command (con, tag, len,
con->recvbuf->data + con->recvbuf->consumed + 4);
/* mark data as processed */
con->recvbuf->consumed += 4 + len;
}
if (con->recvbuf->consumed)
{
n = con->recvbuf->datasize - con->recvbuf->consumed;
if (n > 0)
{
/* shift down unprocessed data */
memmove (con->recvbuf->data,
con->recvbuf->data + con->recvbuf->consumed, n);
}
con->recvbuf->datasize = n;
con->recvbuf->consumed = 0; /* reset */
}
}